package com.business.user.aspect;

import com.business.common.dto.R;
import com.business.common.enums.ErrorEnum;
import com.business.common.exception.CustomException;
import com.business.common.utils.StrKit;
import com.business.redis.ExpiresTimeEnum;
import com.business.redis.constant.RedisKeyConstant;
import com.business.redis.utils.RedisKeysUtil;
import com.business.redis.utils.RedisUtil;
import com.business.user.annotation.FrequentRequest;
import com.business.user.annotation.TokenPermission;
import com.business.user.entity.UserDto;
import com.business.user.utils.JWTUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;


@Aspect
@Order(1)
@Component
public class RequestTokenAspect {

    @Pointcut(value = "@within(com.business.user.annotation.TokenPermission) || @annotation(com.business.user.annotation.TokenPermission)")
    public void tokenPoint() {

    }

    private static String[] getKeys(String token) {
        int index = token.indexOf(".");
        String userId = token.substring(0, index);
        token = token.substring(index + 1);
        String redisKey = String.format(RedisKeyConstant.USER_LOGIN_USER, userId);
        String[] keys = new String[]{
                redisKey,
                token
        };
        return keys;
    }


    @Around("tokenPoint()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        MethodSignature joinPointObject = (MethodSignature) joinPoint.getSignature();
        Method method = joinPointObject.getMethod();

        TokenPermission tokenPermission = method.getAnnotation(TokenPermission.class);
        //校验token 获取用户
        String token = request.getHeader("token");

        boolean isNull = StrKit.isBlank(token) || "[object Null]".equals(token);
        //不是必须登陆
        if (tokenPermission != null && !tokenPermission.required() && isNull) {
            return joinPoint.proceed();
        }
        if (isNull) {
            throw new CustomException(ErrorEnum.AUTH_ERROR);
        }
        String[] keys = getKeys(token);
        UserDto userDto = (UserDto) RedisUtil.getObject(keys[0], UserDto.class);
        if (userDto == null) {
            userDto = JWTUtil.parseJWT(keys[1]);
            RedisUtil.setValue(keys[0], userDto, ExpiresTimeEnum.LOGIN.getExpiresTime());
        } else {
            //修改过期时间
            RedisUtil.expire(keys[0], ExpiresTimeEnum.LOGIN.getExpiresTime());
        }
        //控制操作频率
        FrequentRequest frequentRequest = method.getAnnotation(FrequentRequest.class);
        if (frequentRequest != null) {
            String frequentKey = RedisKeysUtil.getKey(method.getDeclaringClass(), method.getName() + "_Request", userDto.getId());
            Integer frequentValue = (Integer) RedisUtil.getValue(frequentKey);
            //一分钟内可以访问的次数
            int value = frequentRequest.value();
            if (frequentValue != null && frequentValue >= value) {
                RedisUtil.expire(frequentKey, ExpiresTimeEnum.ADD_MESSAGE.getExpiresTime());
                return R.error(ErrorEnum.FREQUENT_OPERATION);
            }
            //统计数量
            RedisUtil.incr(frequentKey);
            if (frequentValue == null) {
                RedisUtil.expire(frequentKey, ExpiresTimeEnum.ADD_MESSAGE.getExpiresTime());
            }

        }
        Object[] params = joinPoint.getArgs();
        //设置参数
        for (int i = 0; i < params.length; i++) {
            if (params[i] instanceof UserDto) {
                params[i] = userDto;
                break;
            }
        }
        //重新设置参数
        return joinPoint.proceed(params);
    }

}
